/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include ".\mmachineinfolist.h"

#include <algorithm>

//---------------------------------------------------------------------------

MMachineInfoList::MMachineInfoList(void)
{
}

MMachineInfoList::~MMachineInfoList(void)
{
	CleanUp();
}

//---------------------------------------------------------------------------

void MMachineInfoList::CleanUp(void)
{
	MMachineInfo *mmi;

	for(iter=theVector.begin(); iter!=theVector.end(); iter++){
		mmi = *iter;
		if(mmi!=NULL){
			*iter = NULL;
			delete mmi;
		}
	}
	theVector.clear();
}

//---------------------------------------------------------------------------

MMachineInfo* MMachineInfoList::CreateMachineInfo(void)
{
	MMachineInfo *mmi;

	try{
		mmi = new MMachineInfo;
	}
	catch(...){
		mmi = NULL;
	}

	if(mmi!=NULL)
		mmi->Initialize();

	return mmi;
}

//---------------------------------------------------------------------------

MMachineInfo* MMachineInfoList::GetMachineInfoByMacAddr(MAddressMAC* macIn)
{
	MMachineInfo* mmi;
	bool found;

	mmi = NULL;
	found = false;
	for(iter=theVector.begin(); iter!=theVector.end() && !found; iter++){
		mmi = *iter;
		if(mmi!=NULL)
			found = mmi->address.addrMAC.IsEqualWith(macIn);
	}

	return found ? mmi : NULL;
}

MMachineInfo* MMachineInfoList::GetMachineInfoByIpAddr(DWORD ipIn)
{
	MMachineInfo* mmi;
	bool found;

	mmi = NULL;
	found = false;
	for(iter=theVector.begin(); iter!=theVector.end() && !found; iter++){
		mmi = *iter;
		if(mmi!=NULL)
			found = mmi->address.addrIP.GetD()==ipIn;
	}

	return found ? mmi : NULL;
}

MMachineInfo* MMachineInfoList::GetMachineInfoByIpAddr6(in_addr6* ia6In)
{
	MMachineInfo* mmi;
	bool found;

	if(ia6In==NULL) return NULL;

	mmi = NULL;
	found = false;
	for(iter=theVector.begin(); iter!=theVector.end() && !found; iter++){
		mmi = *iter;
		if(mmi!=NULL)
			found = mmi->address.addrIPv6.IsEqualWith(ia6In);
	}

	return found ? mmi : NULL;
}

//---------------------------------------------------------------------------

MMachineInfo* MMachineInfoList::GetMachineInfoByIp(MMachineInfo* mmiIn)
{
	MMachineInfo *mmi;
	in_addr6 ia6;

	if(mmiIn==NULL) return NULL;

	if(!mmiIn->address.addrIP.IsEmpty()){
		mmi = GetMachineInfoByIpAddr(mmiIn->address.addrIP.GetD());
		if(mmi==NULL){
			// not found by IP, maybe by MAC ?
			mmi = GetMachineInfoByMac(mmiIn);
			if(mmi!=NULL){
				// found by MAC, is the IP taken ?
				if(!mmi->address.addrIP.IsEmpty()){
					// the IP is taken, and is not what I am searching for
					// otherway the first call should have found it
					mmi = NULL;
				}
			}
		}
		return mmi;
	}

	if(!mmiIn->address.addrIPv6.IsEmpty()){
		mmiIn->address.addrIPv6.Get(&ia6);
		mmi = GetMachineInfoByIpAddr6(&ia6);
		if(mmi==NULL){
			// not found by IPv6, maybe by MAC ?
			mmi = GetMachineInfoByMac(mmiIn);
			if(mmi!=NULL){
				// found by MAC, is the IPv6 taken ?
				if(!mmi->address.addrIPv6.IsEmpty()){
					// the IPv6 is taken, and is not what I am searching for
					// otherway the first call should have found it
					mmi = NULL;
				}
			}
		}
		return mmi;
	}

	if(!mmiIn->address.addrIPX.IsEmpty()){
		mmi = GetMachineInfoByMac(mmiIn);
		if(mmi!=NULL){
			if(mmiIn->address.addrIPX.IsEqualWith(&(mmi->address.addrIPX)))
				return mmi;
			// same MAC another IPX address
			mmi = NULL;
		}
		return mmi;
	}

	if(!mmiIn->address.addrMAC.IsEmpty())
		return GetMachineInfoByMac(mmiIn);

	return NULL;
}

MMachineInfo* MMachineInfoList::GetMachineInfoByMac(MMachineInfo* mmiIn)
{
	MMachineInfo *mmi;
	MAddressMAC macIn;
	bool found;

	if(mmiIn==NULL) return NULL;

	if(mmiIn->address.gwMAC) return NULL;					// do NOT compare machines with MAC unknown
	if(mmiIn->address.addrMAC.IsEmpty()) return NULL;		// do NOT compare machines with empty MAC

	macIn.LoadFromPointer(&mmiIn->address.addrMAC);

	mmi = NULL;
	found = false;
	for(iter=theVector.begin(); iter!=theVector.end() && !found; iter++){
		mmi = *iter;
		if(mmi!=NULL){
			if(macIn.IsEqualWith(&(mmi->address.addrMAC)))
//			if(macIn.Compare(&mmi->address.addrMAC)==0)
				found = true;
		}
	}

	return found ? mmi : NULL;
}

//---------------------------------------------------------------------------

bool MMachineInfoList::Add(MMachineInfo* mmiIn)
{
	MMachineInfo *mmi;
	int cnt;
	bool owned;
	DWORD dwVal;

	if(mmiIn==NULL) return true;	// nothing to add

	dwVal = mmiIn->address.addrIP.GetD();
	if(dwVal==0) mmi = GetMachineInfoByMac(mmiIn);		// IP not set, it is OK to search by MAC
	else         mmi = GetMachineInfoByIp(mmiIn);
/*
	Don not do this:
	if(mmi==NULL)
		mmi = GetMachineInfoByMac(mmiIn);
	because it will show only the first IP for the machines with multiple IP addresses for a single MAC
*/
	if(mmi==NULL){
		// a new IP so I am creating a new machine
		mmi = CreateMachineInfo();
		if(mmi==NULL) return false;
		owned = true;
	}
	else owned = false;

	if(!mmi->LoadFromPointerUpdate(mmiIn)){
		if(owned) delete mmi;
		return false;
	}

	if(owned){
		cnt = (int)theVector.size();
		theVector.push_back(mmi);
		cnt = (int)theVector.size() - cnt;
		if(cnt!=1){
			delete mmi;
			return false;
		}
	}

	return true;
}

bool MMachineInfoList::AddIPv6(MMachineInfo* mmiIn)
{
	MMachineInfo *mmi;
	int cnt;
	bool owned;

	if(mmiIn==NULL) return true;	// nothing to add

	mmi = GetMachineInfoByIp(mmiIn);
//	mmi = GetMachineInfoByMac(mmiIn);
/*
	Don not do this:
	if(mmi==NULL)
		mmi = GetMachineInfoByMac(mmiIn);
	because it will show only the first IP for the machines with multiple IP addresses for a single MAC
*/
	if(mmi==NULL){
		// a new IP so I am creating a new machine
		mmi = CreateMachineInfo();
		if(mmi==NULL) return false;
		owned = true;
	}
	else owned = false;

	if(!mmi->LoadFromPointerUpdate(mmiIn)){
		if(owned) delete mmi;
		return false;
	}

/*
	mmi->address.alive = true;
	mmi->address.gwMAC = false;
	mmi->address.respReceived = true;
	mmi->address.addrMAC.LoadFromPointer(&mmiIn->address.addrMAC);
	mmi->address.addrIPv6.LoadFromPointer(&mmiIn->address.addrIPv6);
*/

	if(owned){
		cnt = (int)theVector.size();
		theVector.push_back(mmi);
		cnt = (int)theVector.size() - cnt;
		if(cnt!=1){
			delete mmi;
			return false;
		}
	}

	return true;
}

void MMachineInfoList::Clear(void)
{
	CleanUp();
}

//---------------------------------------------------------------------------

MMachineInfo* MMachineInfoList::GetCreateMachineInfo(DWORD ipIn)
{
	MMachineInfo* mmi;
	int cnt;

	mmi = GetMachineInfoByIpAddr(ipIn);
	if(mmi!=NULL) return mmi;

	mmi = CreateMachineInfo();
	if(mmi==NULL) return NULL;

	mmi->address.addrIP.Set(ipIn);

	cnt = (int)theVector.size();
	theVector.push_back(mmi);
	cnt = (int)theVector.size() - cnt;
	if(cnt!=1){
		delete mmi;
		return NULL;
	}

	return mmi;
}

MMachineInfo* MMachineInfoList::GetCreateMachineInfoEx(MFullAddress* addrIn, bool* createdOut)
{
	MMachineInfo* mmi;
	in_addr6 ia6;
	int cnt;

	mmi = NULL;

	if(addrIn==NULL){
		*createdOut = false;
		return mmi;
	}

	// search by IPv4
	if(!addrIn->addrIP.IsEmpty()){
		mmi = GetMachineInfoByIpAddr(addrIn->addrIP.GetD());
		if(mmi!=NULL){
			*createdOut = false;
			return mmi;
		}
	}

	// search by IPv6
	if(!addrIn->addrIPv6.IsEmpty()){
		addrIn->addrIPv6.Get(&ia6);
		mmi = GetMachineInfoByIpAddr6(&ia6);
		if(mmi!=NULL){
			*createdOut = false;
			return mmi;
		}
	}

	// search by MAC, ONLY if addrIP and addrIPv6 are EMPTY !
	if(!addrIn->addrMAC.IsEmpty() && addrIn->addrIP.IsEmpty() && addrIn->addrIPv6.IsEmpty()){
		mmi = GetMachineInfoByMacAddr(&(addrIn->addrMAC));
		if(mmi!=NULL){
			*createdOut = false;
			return mmi;
		}
	}

	// not found, must be created
	mmi = CreateMachineInfo();
	if(mmi==NULL) return NULL;

	mmi->address.LoadFromPointer(addrIn);

	cnt = (int)theVector.size();
	theVector.push_back(mmi);
	cnt = (int)theVector.size() - cnt;
	if(cnt!=1){
		delete mmi;
		return NULL;
	}

	*createdOut = true;
	return mmi;
}

//---------------------------------------------------------------------------

int MMachineInfoList::GetSize(void)
{
	return (int)theVector.size();
}

MMachineInfo* MMachineInfoList::GetByIdx(int idxIn)
{
	if(idxIn<0)          return NULL;
	if(idxIn>=GetSize()) return NULL;

	return theVector[idxIn];
}

//---------------------------------------------------------------------------

void MMachineInfoList::SortThePorts(void)
{
	MMachineInfo *mmi;

	for(iter=theVector.begin(); iter!=theVector.end(); iter++){
		mmi = *iter;
		if(mmi!=NULL){
			mmi->SortPortInfo();
			mmi->SortProtoInfo();
			mmi->SortIgmpPacks();
			mmi->SortShareInfo();
		}
	}
}

bool MMachineInfoGreaterIp(MMachineInfo* a1, MMachineInfo* a2)
{
	DWORD ip1, ip2;

	if(a2==NULL) return true;
	if(a1==NULL) return false;
	
	ip1 = a1->address.addrIP.GetD();
	ip2 = a2->address.addrIP.GetD();

	return htonl(ip1) < htonl(ip2);
}
bool MMachineInfoGreaterMAC(MMachineInfo* a1, MMachineInfo* a2)
{
	CString s1, s2;
	DWORD ip1, ip2;
	int compVal;

	if(a2==NULL) return true;
	if(a1==NULL) return false;
	
	s1 = a1->address.addrMAC.GetS();
	s2 = a2->address.addrMAC.GetS();
	try{
		compVal = s1.CompareNoCase(s2);
	}
	catch(...){
		compVal = 0;
	}

	if(compVal==0){
		ip1 = a1->address.addrIP.GetD();
		ip2 = a2->address.addrIP.GetD();
		return htonl(ip1) < htonl(ip2);
	}

	return compVal<0 ? true : false;
}
/*
bool MMachineInfoGreaterName(MMachineInfo* a1, MMachineInfo* a2)
{
	DWORD ip1, ip2;
	int compVal;

	if(a2==NULL) return true;
	if(a1==NULL) return false;

	try{
		compVal = a1->nameDns.CompareNoCase(a2->nameDns);
	}
	catch(...){
		compVal = 0;
	}

	if(compVal==0){
		ip1 = a1->address.addrIP.GetD();
		ip2 = a2->address.addrIP.GetD();
		return htonl(ip1) < htonl(ip2);
	}

	return compVal<0 ? true : false;
}
*/

void MMachineInfoList::SortTheListByIp(void)
{
	std::sort(theVector.begin(), theVector.end(), MMachineInfoGreaterIp);
	SortThePorts();
}
void MMachineInfoList::SortTheListByMAC(void)
{
	std::sort(theVector.begin(), theVector.end(), MMachineInfoGreaterMAC);
	SortThePorts();
}
/*
void MMachineInfoList::SortTheListByName(void)
{
	std::sort(theVector.begin(), theVector.end(), MMachineInfoGreaterName);
	SortThePorts();
}
*/

void MMachineInfoList::RandomizeList(void)
{
	std::random_shuffle(theVector.begin(), theVector.end());
}

//---------------------------------------------------------------------------

/*
[machine]
	...
	...
	...
	[ports]
		xx(method) xxxxx(port) xxxxx(time)
*/

bool MMachineInfoList::SaveToFile(CString fileNameIn)
{
	MMachineInfo *mmi;

	// OpenFile(fileNameIn);

	for(iter=theVector.begin(); iter!=theVector.end(); iter++){
		mmi = *iter;
		if(mmi!=NULL){
			// SaveMachine(mmi);
		}
	}

	// CloseFile();

	return true;
}

//---------------------------------------------------------------------------

bool MMachineInfoList::LoadFromFile(CString fileNameIn)
{
	return true;
}

//---------------------------------------------------------------------------
